/*    file: main.cpp
 *    desc:
 * 
 * created: 2016-04-06
 *  author: chuanjiang.zh@qq.com
 * company: 
 */

#include <stdio.h>
#include <iostream>
#include <fstream>

#include "BasicType.h"
#include "SharedPtr.h"
#include "TFileUtil.h"
#include <assert.h>
#include "TByteBuffer.h"
#include "TStringUtil.h"

#include "TThread.h"
#include "TEvent.h"
#include "TCriticalSection.h"
#include <atomic>
#include <deque>
#include <utility>

#include <stdint.h>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>

#include "PsCaster.h"
#include "MediaPacket.h"


#include "Ffmpeg.h"
extern "C"
{
#include "libavutil/time.h"
}


class CasterChannel : public comn::Thread
{
	static std::atomic_int s_seq;

public:
	CasterChannel():
		m_ifs(),
		m_file(),
		m_handle(-1)
	{
	}

	~CasterChannel()
	{
		close();
	}

	virtual int run()
	{
		while (!m_canExit)
		{
			av::MediaPacketPtr pkt = read();
			if (!pkt)
			{
				break;
			}

			if (pkt->size == 25 && pkt->flags == 1)
			{
				//continue;
			}

			MPacket packet;
			memset(&packet, 0, sizeof(packet));
			packet.data = pkt->data;
			packet.size = pkt->size;
			packet.type = pkt->type;
			packet.pts = pkt->pts;
			packet.duration = 0;
			packet.flags = pkt->flags;

			packet.pts -= 469959889274;

			printf("type:%d, size:%d, flags:%d, pts:%lld\n", packet.type, packet.size, packet.flags, packet.pts);

			std::string data = comn::StringCast::toHexGroupString(packet.data, 16);
			printf("%s\n", data.c_str());

			comn::FileUtil::write(packet.data, packet.size, "in.h264", true);

			pscaster_write(m_handle, &packet);

			m_event.timedwait(30);
		}
		return 0;
	}

	virtual void doStop()
	{
		m_event.post();
	}

	bool open(const std::string& indexPath, const std::string& dataPath)
	{
		if (!openFile(indexPath, dataPath))
		{
			return false;
		}

		int port = 10000 + s_seq ++;
		if (!openCaster(port))
		{
			return false;
		}

		return start();
	}

	void close()
	{
		if (isRunning())
		{
			stop();
		}

		closeCaster();
		closeFile();
	}

	bool openFile(const std::string& indexPath, const std::string& dataPath)
	{
		m_ifs.open(indexPath.c_str());
		m_file = fopen(dataPath.c_str(), "rb");
		return m_ifs.is_open() && (m_file != NULL);
	}

	void closeFile()
	{
		m_ifs.close();

		if (m_file)
		{
			fclose(m_file);
			m_file = NULL;
		}
	}

	av::MediaPacketPtr read()
	{
		av::MediaPacketPtr pkt;

		std::string line;
		std::getline(m_ifs, line);
		if (m_ifs.eof())
		{
			return av::MediaPacketPtr();
		}

		pkt.reset(new av::MediaPacket());
		if (!parseLine(line, pkt))
		{
			return av::MediaPacketPtr();
		}

		int size = pkt->size;

		pkt->ensure(size);

		int length = fread(pkt->data, 1, size, m_file);
		if (length != size)
		{
			return av::MediaPacketPtr();
		}

		pkt->size = length;

		return pkt;
	}

	bool parseLine(const std::string& line, av::MediaPacketPtr& pkt)
	{
		std::vector< std::string > segments;
		comn::StringUtil::split(line, ',', segments);
		if (segments.size() < 5)
		{
			return false;
		}

		comn::StringCast::toValue(segments[0], pkt->type);
		comn::StringCast::toValue(segments[1], pkt->size);
		comn::StringCast::toValue(segments[2], pkt->duration);
		comn::StringCast::toValue(segments[3], pkt->pts);
		comn::StringCast::toValue(segments[4], pkt->flags);

		return true;
	}

	bool openCaster(int port)
	{
		MFormat format;
		memset(&format, 0, sizeof(format));
		format.codec = MCODEC_H264;
		format.width = 640;
		format.height = 480;
		format.clockRate = 1000000;

		format.channels = 1;
		format.sampleRate = 8000;

		PsCasterParams params;
		params.payload = 96;
		params.ssrc = 1;

		int ret = pscaster_openEx(&m_handle, PSCASTER_PROTOCOL_RTP, 0, &format, &params);

		int rc = pscaster_add_target(m_handle, "192.168.3.53", port);
		return (rc == 0);
	}

	void closeCaster()
	{
		if (m_handle > 0)
		{
			pscaster_close(m_handle);
			m_handle = -1;
		}
	}

	std::ifstream	m_ifs;
	FILE*	m_file;
	caster_t m_handle;
	comn::Event	m_event;

};

std::atomic_int CasterChannel::s_seq(0);

typedef std::shared_ptr< CasterChannel >	CasterChannelPtr;
typedef std::deque< CasterChannelPtr >		CasterChannelArray;

class Application
{
public:
	Application()
	{
		pscaster_init();
	}

	~Application()
	{
		pscaster_quit();
	}

	void add(const std::string& indexPath, const std::string& dataPath)
	{
		CasterChannelPtr channel(new CasterChannel());
		channel->open(indexPath, dataPath);

		comn::AutoCritSec lock(m_cs);
		m_channels.push_back(channel);
	}

	void remove()
	{
		CasterChannelPtr chl = removeOne();
		if (chl)
		{
			chl->close();
		}
	}

	CasterChannelPtr removeOne()
	{
		CasterChannelPtr chl;
		comn::AutoCritSec lock(m_cs);
		if (m_channels.size() > 0)
		{
			chl = m_channels.front();
			m_channels.pop_front();
		}
		return chl;
	}

	bool empty()
	{
		comn::AutoCritSec lock(m_cs);
		return m_channels.empty();
	}

	CasterChannelArray	m_channels;

	comn::CriticalSection	m_cs;

};

void addChannel(Application& app, const std::string& indexPath, const std::string& dataPath)
{
	app.add(indexPath, dataPath);
}

void removeChannel(Application& app)
{
	app.remove();
}

typedef std::shared_ptr< std::thread >	ThreadPtr;
typedef std::deque< ThreadPtr >		ThreadList;

int main(int argc, char** argv)
{
	if (argc <= 2)
    {
		printf("DemoPsMux pkt.txt camera.h264");
        return EINVAL;
    }

	std::string indexPath = argv[1];
	std::string dataPath = argv[2];

	Application app;

	for (int i = 0; i < 15; i++)
	{
		app.add(indexPath, dataPath);
	}

	ThreadList threads;

	//while (!app.empty())
	{
		
		for (int i = 0; i < 50; i++)
		{
			ThreadPtr th(new std::thread(addChannel, std::ref(app), indexPath, dataPath));
			ThreadPtr th2(new std::thread(removeChannel, std::ref(app)));
			threads.push_back(th);
			threads.push_back(th2);
		}

		av_usleep(1000 * 30);
	}

	std::cin.get();

	for (size_t i = 0; i < threads.size(); i++)
	{
		threads[i]->join();
	}
	threads.clear();

	return 0;
}




